import { config } from './Config';

const { ccclass, property } = cc._decorator;


/**
 *点击节点的对象
 *
 * @interface touchNodeObj
 */
declare interface touchNodeObj {
    cNode: cc.Node,  // 点中的节点
    index: number,   // 点中节点在数组中的索引
}
@ccclass
export default class Game extends cc.Component {

    @property(cc.Node)
    node_p: cc.Node = null;     // 9个格子的父节点

    @property(cc.Node)
    node_m: cc.Node = null;     // 两个layout移动板的父节点

    @property(cc.Node)
    node_v: cc.Node = null;      // 竖直方向的layout

    @property(cc.Node)
    node_h: cc.Node = null;     // 水平方向的layout

    @property(cc.Label)
    lblScore: cc.Label = null;     // 水平方向的layout

    @property(cc.Prefab)
    magicBox_prefab: cc.Node = null;     // 格子预设

    @property(cc.SpriteFrame)
    iconList: cc.SpriteFrame[] = []; // 5个游戏图标

    /*—————————————初始化数据—————————————————————— */
    nodeList: cc.Node[] = [];       // 游戏界面的 9 个格子
    dataList = [];//二维数组的数据 a0是最中间的数据  1上 2右 3下 4左
    node_pool: cc.NodePool = null;

    /*—————————————点击移动使用—————————————————————— */
    cnodeObj: touchNodeObj = null;      // 当按下鼠标的时候，点中的 格子 和 数组中的索引 {cnode,index}
    direct: number = 0; // 移动防线
    miconList = []; // 移动节点的对象数组 {行，列，图片类型}
    lastPos = null;// 上一次移动的点
    score: number = 0;  //移动步数

    /**
     * 数组乱序
     * 
     * @param {*} arr
     * @returns
     * @memberof Game
     */
    shuffle(arr) {
        for (let j, x, i = arr.length; i; j = Math.floor(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
        return arr;
    }

    /**
     * 创建格子
     *
     * @param {cc.Node} parentNode
     * @param {cc.SpriteFrame} [icon]
     * @memberof Game
     */
    createEnemy(parentNode: cc.Node, icon?: cc.SpriteFrame) {
        let enemy = null;
        if (this.node_pool.size() > 0) { // 通过 size 接口判断对象池中是否有空闲的对象
            enemy = this.node_pool.get();
        } else { // 如果没有空闲对象，也就是对象池中备用对象不够时，我们就用 cc.instantiate 重新创建
            enemy = cc.instantiate(this.magicBox_prefab);
        }
        enemy.parent = parentNode; // 将生成的敌人加入节点树

        //接下来就可以调用 enemy 身上的脚本进行初始化
        if (icon) {
            let iconNode = enemy.getChildByName('icon');
            if (iconNode) {
                iconNode.getComponent(cc.Sprite).spriteFrame = icon;
            } else {
                cc.log('----iconnode不存在---');
            }
        }

        return enemy;
    }

    /**
     * 将对象返回对象池
     *
     * @param {cc.Node} enemy
     * @memberof Game
     */
    onEnemyKilled(enemy: cc.Node) {
        // enemy 应该是一个 cc.Node
        this.node_pool.put(enemy); // 和初始化时的方法一样，将节点放进对象池，这个方法会同时调用节点的 removeFromParent
    }

    /**
     * 清楚对象池
     *
     * @memberof Game
     */
    clearPool() {
        this.node_pool.clear(); // 调用这个方法就可以清空对象池
    }

    onLoad() {
        this.node_pool = new cc.NodePool();
        //初始化对象池
        let pNum = 3 * config.curLevel * config.curLevel;
        for (let i = 0; i < pNum; i++) {
            let enemy = cc.instantiate(this.magicBox_prefab); // 创建节点
            this.node_pool.put(enemy); // 通过 put 接口放入对象池
        }
    }
    resetData() {
        this.score = 0;
        this.lblScore.string = '0';
        let mcount = config.curLevel * config.curLevel;
        // 先生成新的 一维数组 
        let resetDataLit = [];
        for (let i = 0; i < 5; i++) {
            for (let j = 0; j < mcount; j++) {
                resetDataLit.push(i);
            }
        }
        // 把一维数组打乱
        resetDataLit = this.shuffle(resetDataLit);

        // 把一维数组的值 赋值给 二维数组 dataList
        for (let i = 0; i < 5; i++) {
            let arr = [];
            for (let j = 0; j < mcount; j++) {
                let b = i * mcount + j;
                let icon = resetDataLit[b];
                arr.push(icon);
            }
            this.dataList.push(arr);
        }
        // 刷新游戏界面的格子
        this.node_p.removeAllChildren();
        this.nodeList = [];
        for (let i = 0; i < mcount; i++) {
            // let mb = cc.instantiate(this.magicBox_prefab);
            // let iconNode = mb.getChildByName('icon');
            // if (iconNode) {
            //     iconNode.getComponent(cc.Sprite).spriteFrame = this.iconList[this.dataList[0][i]];
            // } else {
            //     cc.log('----iconnode不存在---');
            // }
            // mb.parent = this.node_p;
            let mb = this.createEnemy(this.node_p, this.iconList[this.dataList[0][i]]);
            this.nodeList.push(mb);
        }
        // 重新设置 格子 和 移动的layout父节点 的 宽度，用于layout的布局
        this.node_p.width = this.nodeList[0].width * config.curLevel;
        this.node_m.width = this.nodeList[0].width * config.curLevel;
        this.node_m.height = this.nodeList[0].height * config.curLevel;
        // 重置一下 横竖 的layout的宽高,用于如果格子的大小改变的话，就可以自适应
        this.node_h.height = this.nodeList[0].height;
        this.node_v.width = this.nodeList[0].width;
    }

    start() {
        this.resetData();
        this.bindEvent();
    }

    bindEvent() {
        this.node.on(cc.Node.EventType.TOUCH_START, (event: cc.Event.EventTouch) => {
            // 当前点击位置
            let lPos = event.getLocation();
            // 当前点击节点
            this.cnodeObj = this.getMpdeBuTouch(lPos);
        }, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, (event: cc.Event.EventTouch) => {
            if (!this.cnodeObj) return false;

            let oldPos = event.getStartLocation();
            let nowPos = event.getLocation();
            let box = this.node_p.getBoundingBoxToWorld();

            let dx = nowPos.x - oldPos.x;
            let dy = nowPos.y - oldPos.y;

            // 如果现在还没有 确定 移动的格子，并且 横竖方向移动都没有超过 开始移动的距离
            if (this.miconList.length <= 0 && (Math.abs(dx) > 10 || Math.abs(dy) > 10)) {
                if (Math.abs(dx) > Math.abs(dy)) {
                    // cc.log('横向移动' + nowPos);
                    this.direct = 0;

                    this.getListByNode();
                    this.node_h.active = true;
                    this.node_h.removeAllChildren();
                    this.node_h.setPosition(cc.v2(0, this.cnodeObj.cNode.y));
                } else {
                    // cc.log('纵向移动' + nowPos);
                    this.direct = 1;

                    this.getListByNode();
                    this.node_v.active = true;
                    this.node_v.removeAllChildren();
                    this.node_v.setPosition(cc.v2(this.cnodeObj.cNode.x, 0));
                }


                // 根据要移动的格子列表，创建 节点到 layout 中
                this.miconList.forEach((obj) => {
                    // let lnode = cc.instantiate(this.magicBox_prefab);
                    // let iconNode = lnode.getChildByName('icon');
                    // if (iconNode) iconNode.getComponent(cc.Sprite).spriteFrame = this.iconList[obj.icon];
                    // lnode.setPosition(cc.v2(0, 0));
                    // lnode.parent = this.direct === 0 ? this.node_h : this.node_v;

                    let parentNode = this.direct === 0 ? this.node_h : this.node_v;
                    this.createEnemy(parentNode, this.iconList[obj.icon]);
                });
            }

            // 确定方向，移动 layout 
            if (this.direct === 0) {
                // 如果当前的坐标点，超出 box矩形框的坐标范围，就不能移动
                if (nowPos.x > (box.x + box.width) || nowPos.x < box.x) {
                    cc.log('---------不能移动x-----');
                    return false;
                }
                // 计算当前坐标点 和 点击位置 的X方向的距离
                let dddx = nowPos.x > oldPos.x ? nowPos.sub(oldPos).x : -oldPos.sub(nowPos).x;
                // 如果该距离，大于了 移动的最大格子数量
                if (Math.abs(dddx) > this.nodeList[0].width * (config.curLevel - 1)) {
                    cc.log('---------超出最大移动距离x-----');
                    return false;
                }
                this.node_h.x = dddx;
            } else {

                if (nowPos.y > (box.y + box.height) || nowPos.y < box.y) {
                    cc.log('---------不能移动y-----');
                    return false;
                }
                let ddds = nowPos.y > oldPos.y ? nowPos.sub(oldPos).y : -oldPos.sub(nowPos).y;
                if (Math.abs(ddds) > this.nodeList[0].height * (config.curLevel - 1)) {
                    cc.log('---------超出最大移动距离y-----');
                    return false;
                }
                this.node_v.y = ddds;
            }
            // 移动完成后，记录一下当前移动的点
            this.lastPos = nowPos;
            // this.moveNode(delta);
        }, this);

        this.node.on(cc.Node.EventType.TOUCH_END, this.touchCNodeEnd, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.touchCNodeEnd, this);
    }

    touchCNodeEnd(event: cc.Event.EventTouch) {
        if (!this.cnodeObj || !this.lastPos) return false;
        let nowPos = this.lastPos;
        let oldPos = event.getStartLocation();
        let dg = this.getMoveBoxNum(nowPos, oldPos);

        if (dg > 0) {
            this.sortMoveEnd(dg, nowPos, oldPos);
            this.updateMoveIcon();
            // 是否结束
            this.gameOver();
        }
        this.resetMoveData();
    }

    /**
     * 是否结束游戏
     *
     * @memberof Game
     */
    gameOver() {
        this.score++;
        this.lblScore.string = this.score.toString();
        let isOver = true;
        let same = this.dataList[0][0];
        for (let i = 1; i < this.dataList[0].length; i++) {
            if (this.dataList[0][0] !== this.dataList[0][i]) {
                isOver = false;
                break;
            }
        }

        if (isOver) {
            cc.log('游戏结束');
        }
    }


    /**
     *移动结束后，如果移动个数大于0，排列新的数据
     *
     * @param {cc.Event.EventTouch} event
     * @memberof Game
     */
    sortMoveEnd(dg: number, nowPos: cc.Vec2, oldPos: cc.Vec2) {
        let temp = [];
        let temp2 = [];
        // 取出移动列表中的 icon 类型 
        this.miconList.forEach((obj) => {
            temp2.push(obj.icon);
        });
        // 根据方向， 把移动列表中的  icon 类型，按方向重新进行排序
        if (this.direct === 0) {
            if (nowPos.x > oldPos.x) {// 把后面放前面来
                temp = temp2.splice(temp2.length - dg, temp2.length);
                temp2 = temp.concat(temp2);
            } else {// 把前面放后面去
                temp = temp2.splice(0, dg);
                temp2 = temp2.concat(temp);
            }
        } else {
            if (nowPos.y > oldPos.y) {// 把前面放后面去
                temp = temp2.splice(0, dg);
                temp2 = temp2.concat(temp);
            } else {// 把后面放前面来
                temp = temp2.splice(temp2.length - dg, temp2.length);
                temp2 = temp.concat(temp2);
            }
        }

        // 把排序完成的 新的 icon 列表，重新赋值到 数据列表中
        this.miconList.forEach((obj, cg) => {
            this.miconList[cg].icon = temp2[cg];
            this.dataList[obj.row][obj.col] = temp2[cg];
        });
    }

    /**
     *获取移动过程中，移动了多少个 格子
     *
     * @param {cc.Vec2} nowPos
     * @param {cc.Vec2} oldPos
     * @returns
     * @memberof Game
     */
    getMoveBoxNum(nowPos: cc.Vec2, oldPos: cc.Vec2) {

        // 计算移动的长度
        let endPos = null;
        // 根据方向，计算移动的距离
        if (this.direct === 0) {
            endPos = nowPos.x > oldPos.x ? nowPos.sub(oldPos) : oldPos.sub(nowPos);
        } else {
            endPos = nowPos.y > oldPos.y ? nowPos.sub(oldPos) : oldPos.sub(nowPos);
        }
        let len = endPos.mag();
        cc.log('========移动的长度：' + len);

        // 计算，移动方向，每一格子的长度
        let dr = this.direct === 0 ? this.nodeList[0].width : this.nodeList[0].height;
        // 约计算移动了 几个格子
        let dg = Math.floor(len / dr);
        cc.log('=====移动的格子百分比：' + len % dr / dr);
        if ((len % dr / dr) > (0.7)) dg++;
        dg = dg > (config.curLevel - 1) ? (config.curLevel - 1) : dg;
        cc.log('=====移动的格子数：' + dg);

        return dg;
    }

    // 刷新移动的格子
    updateMoveIcon() {
        // 刷新游戏界面
        for (let i = 0; i < this.nodeList.length; i++) {
            let icon = this.nodeList[i].getChildByName('icon');
            if (icon) icon.getComponent(cc.Sprite).spriteFrame = this.iconList[this.dataList[0][i]];
        }
    }

    // 重置移动过程中，产生的数据，或变化的数据
    resetMoveData() {
        // 重置 无点击状态
        this.cnodeObj = null;
        this.lastPos = null;
        this.miconList = [];

        if (this.node_h.childrenCount > 0) {
            this.node_h.children.forEach((node) => {
                this.onEnemyKilled(node);
            });
        }
        if (this.node_v.childrenCount > 0) {
            this.node_v.children.forEach((node) => {
                this.onEnemyKilled(node);
            });
        }
        // this.node_h.removeAllChildren();
        // this.node_v.removeAllChildren();
        this.node_h.setPosition(cc.v2(0, 0));
        this.node_v.setPosition(cc.v2(0, 0));
        this.node_h.active = false;
        this.node_v.active = false;
    }

    /**
     * 取出移动节点的 列表
     * @memberof Game
     */
    getListByNode() {

        // 如果是横向，
        if (this.direct === 0) {// 4 0 2
            let d1 = Math.floor(this.cnodeObj.index / config.curLevel);
            let arr = [];
            // 取出 左边 ，中间，右边，对应的 格子,顺序必须要对应，不然放入layout的时候，顺序不一致
            this.dataList[4].forEach((icon, j) => {
                if (Math.floor(j / config.curLevel) === d1) {
                    arr.push({
                        row: 4,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.dataList[0].forEach((icon, j) => {
                if (Math.floor(j / config.curLevel) === d1) {
                    arr.push({
                        row: 0,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.dataList[2].forEach((icon, j) => {
                if (Math.floor(j / config.curLevel) === d1) {
                    arr.push({
                        row: 2,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.miconList = arr;
        } else {// 1 0 3
            let d2 = this.cnodeObj.index % config.curLevel;
            let arr2 = [];
            // 取出 上边 ，中间，下边，对应的 格子,顺序必须要对应，不然放入layout的时候，顺序不一致
            this.dataList[1].forEach((icon, j) => {
                if (Math.floor(j % config.curLevel) === d2) {
                    arr2.push({
                        row: 1,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.dataList[0].forEach((icon, j) => {
                if (Math.floor(j % config.curLevel) === d2) {
                    arr2.push({
                        row: 0,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.dataList[3].forEach((icon, j) => {
                if (Math.floor(j % config.curLevel) === d2) {
                    arr2.push({
                        row: 3,
                        col: j,
                        icon: icon
                    });
                }
            });
            this.miconList = arr2;
        }
    }


    /**
     * 从节点列表中取出 ，当前点击的 节点
     * @param {cc.Vec2} touch
     * @memberof Game
     */
    getMpdeBuTouch(touch: cc.Vec2) {
        let obj: touchNodeObj = null;
        for (let i = 0; i < this.nodeList.length; i++) {
            let node: cc.Node = this.nodeList[i];
            if (node.isValid) {
                let box = node.getBoundingBoxToWorld();
                if (box.contains(touch)) {
                    obj = {
                        cNode: node,
                        index: i,
                    }
                    break;
                }
            }
        }
        return obj;
    }

    click_btn_close() {
        this.clearPool();
        cc.director.loadScene('HomeScene');
    }
}